package com.superman.thread;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.RuntimeUtil;
import com.superman.thread.func.TakeHavingReturnHaving;
import com.superman.thread.func.TakeHavingReturnNothing;
import com.superman.thread.func.TakeNothingReturnHaving;
import com.superman.thread.func.TakeNothingReturnNothing;
import com.superman.util.base.map.ConcurrentFIFOHashMap;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 任务管理器
 * Author: superman
 * Date: 2024/5/22 13:21
 */
@Slf4j
public class TaskManager {

    /**
     * 任务提交信息
     */
    private Submit submit;
    /**
     * 雪花ID生成器
     */
    private Snowflake snowflake;
    /**
     * 任务表<任务ID,`任务`>
     */
    private Map<String, Task> taskTable;
    /**
     * 待执行任务ID队列
     */
    private Queue<String> taskQueue;
    /**
     * 任务执行线程池
     */
    private ExecutorService taskPool;
    /**
     * 任务配置
     */
    private TaskConfig taskConfig;
    /**
     * 自动执行任务-最大同时执行数量
     */
    private int maxExecutingQuantity;
    /**
     * 自动执行标志
     * false - 当前不是在自动执行
     * true - 当前正在自动执行
     */
    private volatile boolean auto = false;
    /**
     * 当前正在执行的任务的数量
     */
    private final AtomicInteger autoSize = new AtomicInteger(0);

    ////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////// 内部使用的API /////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////

    private TaskManager() {
    }

    /**
     * 雪花算法-生成任务ID
     * 雪花算法的 终端ID、数据中心ID 默认为 当前机器的 mac地址转long
     */
    private String GeneratorTaskId() {
        return this.snowflake.nextIdStr();
    }

    /**
     * 自动提交待执行任务
     */
    private synchronized void autoCommitTask() {
        // false - 当前不是在自动执行
        // true - 当前正在自动执行
        if (this.auto) return;
        this.auto = true;
        String taskId;
        Task task;
        // 任务队列有任务，且未达到最大同时执行数量
        while (this.taskQueue.size() > 0 && this.autoSize.intValue() < this.maxExecutingQuantity) {
            // 从待执行队列中获取 最早的一个任务(队列头部)
            taskId = this.taskQueue.poll();
            task = this.taskTable.get(taskId);
            if (null != task) {
                // 异步执行任务，不会阻塞
                task.asyncTask(this.taskPool);
                this.autoSize.incrementAndGet();
                log.info("自动执行任务【{}】", taskId);
            }
        }
        this.auto = false;
    }

    /**
     * 任务执行完毕的回调
     */
    private void autoCallback() {
        // 当前正在执行的任务的数量-1
        this.autoSize.addAndGet(-1);
        // 自动提交待执行任务
        this.autoCommitTask();
    }

    /**
     * 配置任务参数
     *
     * @param taskConfig 任务配置对象
     */
    private void taskConfiguration(Task task, TaskConfig taskConfig) {
        if (null == taskConfig) return;
        task.setRepeat(taskConfig.isRepeat());
        task.setTimeout(taskConfig.getTimeout());
        if (null != taskConfig.getRepeatStrategy()) {
            task.setRepeatStrategy(taskConfig.getRepeatStrategy());
        }
        if (null != taskConfig.getUnit()) {
            task.setUnit(taskConfig.getUnit());
        }
    }

    /**
     * 提交任务，并等待执行
     *
     * @param sync       是否同步 true-同步(阻塞)、false-异步(不阻塞，添加到待执行队列FIFO，等待自动执行)
     * @param taskConfig 任务配置(是否可重复执行、重复执行阻塞时的执行策略、超时时间)
     */
    private void submitTask(String taskId, Task task, boolean sync, TaskConfig taskConfig) {
        // 配置任务(执行超时时间、是否可重复执行、阻塞时的执行策略)
        this.taskConfiguration(task, taskConfig);
        // 添加到任务表
        this.taskTable.put(taskId, task);
        if (sync) {
            // 同步任务，同步执行，阻塞
            task.syncTask(this.taskPool);
        } else {
            // 设置任务结束后自动执行下一个任务的回调
            task.setAutoNext(this::autoCallback);
            // 添加到待执行队列
            this.taskQueue.offer(taskId);
            // 自动提交待执行任务
            this.autoCommitTask();
        }
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////// 提交任务的API /////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * 提交-无参有返回值的任务
     *
     * @param taskName 任务名称
     * @param sync     是否同步 true-同步(阻塞)、false-异步(不阻塞，添加到待执行队列FIFO，等待自动执行)
     * @param callable 无参有返回值的任务
     * @param <R>      泛型
     * @return 任务ID
     */
    public <R> String submitTask(String taskName, boolean sync, TakeNothingReturnHaving<R> callable) {
        // 生成全局唯一的任务ID
        String taskId = GeneratorTaskId();
        // 创建任务
        Task task = submit.createTask(taskId, taskName, callable);
        // 提交任务，并等待执行
        submitTask(taskId, task, sync, this.taskConfig);
        return taskId;
    }

    /**
     * 提交-无参有返回值的任务
     *
     * @param taskName   任务名称
     * @param sync       是否异步 true-异步(添加到待执行队列FIFO，等待自动执行)
     * @param taskConfig 任务配置(是否可重复执行、重复执行阻塞时的执行策略、超时时间)
     * @param callable   无参有返回值的任务
     * @param <R>        泛型
     * @return 任务ID
     */
    public <R> String submitTask(String taskName, boolean sync, TaskConfig taskConfig, TakeNothingReturnHaving<R> callable) {
        String taskId = GeneratorTaskId();
        Task task = submit.createTask(taskId, taskName, callable);
        submitTask(taskId, task, sync, taskConfig);
        return taskId;
    }

    /**
     * 提交-无参无返回值的任务
     *
     * @param taskName 任务名称
     * @param sync     是否异步 true-异步(添加到待执行队列FIFO，等待自动执行)
     * @param runnable 无参无返回值的任务
     * @return 任务ID
     */
    public String submitTask(String taskName, boolean sync, TakeNothingReturnNothing runnable) {
        String taskId = GeneratorTaskId();
        Task task = submit.createTask(taskId, taskName, runnable);
        submitTask(taskId, task, sync, this.taskConfig);
        return taskId;
    }

    /**
     * 提交-无参无返回值的任务
     *
     * @param taskName   任务名称
     * @param sync       是否异步 true-异步(添加到待执行队列FIFO，等待自动执行)
     * @param taskConfig 任务配置(是否可重复执行、重复执行阻塞时的执行策略、超时时间)
     * @param runnable   无参无返回值的任务
     * @return 任务ID
     */
    public String submitTask(String taskName, boolean sync, TaskConfig taskConfig, TakeNothingReturnNothing runnable) {
        String taskId = GeneratorTaskId();
        Task task = submit.createTask(taskId, taskName, runnable);
        submitTask(taskId, task, sync, taskConfig);
        return taskId;
    }

    /**
     * 提交-有参有返回值的任务
     *
     * @param taskName 任务名称
     * @param sync     是否异步 true-异步(添加到待执行队列FIFO，等待自动执行)
     * @param function 有参有返回值的任务
     * @param param    任务参数对象
     * @param <R>      泛型
     * @return 任务ID
     */
    public <R> String submitTask(String taskName, boolean sync, TakeHavingReturnHaving<R, Object> function, R param) {
        String taskId = GeneratorTaskId();
        Task task = submit.createTask(taskId, taskName, function, param);
        submitTask(taskId, task, sync, this.taskConfig);
        return taskId;
    }

    /**
     * 提交-有参有返回值的任务
     *
     * @param taskName   任务名称
     * @param sync       是否异步 true-异步(添加到待执行队列FIFO，等待自动执行)
     * @param taskConfig 任务配置(是否可重复执行、重复执行阻塞时的执行策略、超时时间)
     * @param function   有参有返回值的任务
     * @param param      任务参数对象
     * @param <R>        泛型
     * @return 任务ID
     */
    public <R> String submitTask(String taskName, boolean sync, TaskConfig taskConfig, TakeHavingReturnHaving<R, Object> function, R param) {
        String taskId = GeneratorTaskId();
        Task task = submit.createTask(taskId, taskName, function, param);
        submitTask(taskId, task, sync, taskConfig);
        return taskId;
    }

    /**
     * 提交-有参无返回值的任务
     *
     * @param taskName 任务名称(任务描述)
     * @param sync     是否异步 true-异步(添加到待执行队列FIFO，等待自动执行)
     * @param consumer 任务主体
     * @param param    任务参数
     * @param <R>      泛型
     * @return 任务ID，凭任务ID查看、操作任务
     */
    public <R> String submitTask(String taskName, boolean sync, TakeHavingReturnNothing<R> consumer, R param) {
        String taskId = GeneratorTaskId();
        Task task = submit.createTask(taskId, taskName, consumer, param);
        submitTask(taskId, task, sync, this.taskConfig);
        return taskId;
    }

    /**
     * 提交-有参无返回值的任务
     *
     * @param taskName   任务名称(任务描述)
     * @param sync       是否异步 true-异步(添加到待执行队列FIFO，等待自动执行)
     * @param taskConfig 任务配置(是否可重复执行、重复执行阻塞时的执行策略、超时时间)
     * @param consumer   任务主体
     * @param param      任务参数
     * @param <R>        泛型
     * @return 任务ID，凭任务ID查看、操作任务
     */
    public <R> String submitTask(String taskName, boolean sync, TaskConfig taskConfig, TakeHavingReturnNothing<R> consumer, R param) {
        String taskId = GeneratorTaskId();
        Task task = submit.createTask(taskId, taskName, consumer, param);
        submitTask(taskId, task, sync, taskConfig);
        return taskId;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////// 其它API ///////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * 获取任务对象
     *
     * @param taskId 任务ID
     * @return 任务对象
     */
    public Task getTask(String taskId) {
        return this.taskTable.get(taskId);
    }

    /**
     * 正在执行的任务数量
     */
    public int executingTasks() {
        return this.autoSize.intValue();
    }

    /**
     * 排队等待执行的任务数量
     */
    public int waitingTasks() {
        return this.taskQueue.size();
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////// 任务管理器构建器 ///////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * 任务管理器构建器
     */
    public static class Builder {
        /**
         * 任务提交信息
         */
        private Submit submit;
        /**
         * 雪花ID生成器
         */
        private Snowflake snowflake;
        /**
         * 任务表<任务ID,`任务`>
         */
        private Map<String, Task> taskTable;
        /**
         * 待执行任务ID队列
         */
        private Queue<String> taskQueue;
        /**
         * 任务执行线程池
         */
        private ExecutorService taskPool;
        /**
         * 任务配置
         */
        private TaskConfig taskConfig;
        /**
         * 自动执行任务-最大同时执行数量
         */
        private Integer maxExecutingQuantity;
        /**
         * 阻塞系数，默认0.5
         * Blocking Coefficient(阻塞系数) = 阻塞时间／（阻塞时间+使用CPU的时间），阻塞因子越大，线程池中的线程数越多。
         * 计算密集型任务的阻塞系数为0，而IO密集型任务的阻塞系数则接近于1。
         * 用于设置线程池参数、最大同时执行任务数
         */
        private float blockingCoefficient = 0.5f;

        ////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////// 任务管理器-默认配置 ////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////

        /**
         * 默认任务提交信息
         */
        private Submit defaultSubmit() {
            return new Submit(NetUtil.getLocalMacAddress(), "system");
        }

        /**
         * 默认雪花ID生成器
         * 默认终端ID 根据当前环境自动生成
         * 默认数据中心ID 根据当前环境自动生成
         */
        private Snowflake defaultSnowflake() {
            return new Snowflake();
        }

        /**
         * 默认任务表-
         * ConcurrentFIFOHashMap，定长的ConcurrentHashMap，自动删除最先存入的对象，默认最大存储10W条记录
         */
        private Map<String, Task> defaultTaskTable() {
            return new ConcurrentFIFOHashMap<>(100000);
        }

        /**
         * 默认待执行任务ID队列-无限队列
         */
        private Queue<String> defaultTaskQueue() {
            return new ConcurrentLinkedDeque<>();
        }

        /**
         * 默认任务执行线程池，默认阻塞系数 0.5
         * 线程池的大小计算公式为：CPU可用核心数 / (1 - 阻塞系数)
         * Blocking Coefficient(阻塞系数) = 阻塞时间／（阻塞时间+使用CPU的时间），阻塞因子越大，线程池中的线程数越多。
         * 计算密集型任务的阻塞系数为0，而IO密集型任务的阻塞系数则接近于1。
         */
        private ExecutorService defaultTaskPool() {
            return ThreadUtil.newExecutorByBlockingCoefficient(this.blockingCoefficient);
        }

        /**
         * 默认任务配置
         * 1、默认不支持重复执行
         * 2、默认任务重复执行阻塞时的策略：废弃后续
         * 3、默认任务超时时间：24H
         */
        private TaskConfig defaultTaskConfig() {
            return TaskConfig.Builder.defaultConfig();
        }

        /**
         * 默认任务最大同时执行数量
         * 至少20个
         */
        private int defaultMaxExecutingQuantity() {
            // 以阻塞系数计算核心线程数
            float poolSize = RuntimeUtil.getProcessorCount() / (1 - blockingCoefficient);
            // 核心线程数超过20个，以核心线程数-1为准，否则默认为20个
            return Math.round(Math.max(20, poolSize - 1));
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////// 任务管理器-配置参数 ////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////

        /**
         * 任务提交信息
         */
        public Builder submit(Submit submit) {
            this.submit = submit;
            return this;
        }

        /**
         * 雪花ID生成器
         */
        public Builder snowflake(Snowflake snowflake) {
            this.snowflake = snowflake;
            return this;
        }

        /**
         * 任务表<任务ID,`任务`>
         */
        public Builder taskTable(Map<String, Task> taskTable) {
            this.taskTable = taskTable;
            return this;
        }

        /**
         * 待执行任务ID队列
         */
        public Builder taskQueue(Queue<String> taskQueue) {
            this.taskQueue = taskQueue;
            return this;
        }

        /**
         * 任务执行线程池
         */
        public Builder taskPool(ExecutorService taskPool) {
            this.taskPool = taskPool;
            return this;
        }

        /**
         * 任务配置
         */
        public Builder taskConfig(TaskConfig taskConfig) {
            this.taskConfig = taskConfig;
            return this;
        }

        /**
         * 自动执行任务-最大同时执行数量
         */
        public Builder maxExecutingQuantity(int maxExecutingQuantity) {
            this.maxExecutingQuantity = maxExecutingQuantity;
            return this;
        }

        /**
         * 构建任务管理器对象
         *
         * @return 任务管理器对象
         */
        public TaskManager build() {
            TaskManager taskManager = new TaskManager();
            if (null != this.submit) {
                taskManager.submit = this.submit;
            } else {
                taskManager.submit = defaultSubmit();
            }
            if (null != this.snowflake) {
                taskManager.snowflake = this.snowflake;
            } else {
                taskManager.snowflake = defaultSnowflake();
            }
            if (null != this.taskTable) {
                taskManager.taskTable = this.taskTable;
            } else {
                taskManager.taskTable = defaultTaskTable();
            }
            if (null != this.taskQueue) {
                taskManager.taskQueue = this.taskQueue;
            } else {
                taskManager.taskQueue = defaultTaskQueue();
            }
            if (null != this.taskPool) {
                taskManager.taskPool = this.taskPool;
            } else {
                taskManager.taskPool = defaultTaskPool();
            }
            if (null != this.taskConfig) {
                taskManager.taskConfig = this.taskConfig;
            } else {
                taskManager.taskConfig = defaultTaskConfig();
            }
            if (null != this.maxExecutingQuantity) {
                taskManager.maxExecutingQuantity = this.maxExecutingQuantity;
            } else {
                taskManager.maxExecutingQuantity = defaultMaxExecutingQuantity();
            }


            return taskManager;
        }

    }


}
